float4x4 World: World;
float4x4 View : View;
float4x4 Projection : Projection;

//color of the light 
float3 Color : Color; 

//position of the camera, for specular light
float3 cameraPosition : CameraPosition; 

//this is used to compute the world-position
float4x4 InvertViewProjection : InvertViewProjection; 

//this is the position of the light
float3 lightPosition : LightPosition;

//how far does this light reach
float lightRadius : LightRadius;

float3 lightDirection : LightDirection;

float lightIntensity : LightIntensity;

float lightDecayExponent : LightDecayExponent;

float lightAngleCosine : LightAngleCosine;

// diffuse color, and specularIntensity in the alpha channel
texture colorMap;
sampler colorSampler = sampler_state
{
	Texture = <colorMap>;
};

// normals, and specularPower in the alpha channel
texture normalMap;
sampler normalSampler = sampler_state
{
	Texture = <normalMap>;
};

//depth
texture depthMap;
sampler depthSampler = sampler_state
{
	Texture = <depthMap>;
};

struct VertexShaderInput
{
	float3 Position : POSITION0;
};

struct VertexShaderOutput
{
	float4 Position : POSITION0;
	float4 ScreenPosition : TEXCOORD0;
};

//======================================//
// For shadow map
//======================================//
float4x4 lightWorldViewProj;

// depth map from light
texture shadowMap;
sampler shadowMapSampler = sampler_state
{
	texture = <shadowMap>;
	MagFilter = LINEAR;
	MinFilter = LINEAR;
	Mipfilter = LINEAR;	
};

texture lightTexture;
sampler lightTexSampler = sampler_state 
{ 
	texture = <lightTexture>;
	MagFilter = LINEAR;
	MinFilter = LINEAR;
	Mipfilter = LINEAR;	
};
//======================================//

VertexShaderOutput VertexShaderFunction(VertexShaderInput input, float3 inNormal: NORMAL0, float2 inTexCoords : TEXCOORD0)
{
	VertexShaderOutput output;
	//processing geometry coordinates
	float4 worldPosition = mul(float4(input.Position, 1), World);
	float4 viewPosition = mul(worldPosition, View);
	output.Position = mul(viewPosition, Projection);
	output.ScreenPosition = output.Position;
	//output.ScreenPosition.xy /= output.ScreenPosition.w;
	
	return output;
}

float2 halfPixel :HalfPixel;
float4 PixelShaderFunction(VertexShaderOutput input) : COLOR0
{
	//obtain screen position
	input.ScreenPosition.xy /= input.ScreenPosition.w;

	//obtain textureCoordinates corresponding to the current pixel
	//the screen coordinates are in [-1,1]*[1,-1]
	//the texture coordinates need to be in [0,1]*[0,1]
	float2 texCoord = 0.5f * (float2(input.ScreenPosition.x, -input.ScreenPosition.y) + 1);
	
	//allign texels to pixels
	texCoord -= halfPixel;

	//read depth
	float depthVal = tex2D(depthSampler, texCoord).r;

	//compute screen-space position
	float4 position;
	position.xy = input.ScreenPosition.xy;
	position.z = depthVal;
	position.w = 1.0f;
	
	//transform to world space
	position = mul(position, InvertViewProjection);
	position /= position.w;

	//surface-to-light vector
	float3 lightVector = lightPosition - position;
	
	//compute attenuation based on distance - linear attenuation
	float attenuation = saturate(1.0f - length(lightVector)/lightRadius); 

	//normalize light vector
	lightVector = normalize(lightVector); 

	float SDL = dot(lightDirection, -lightVector);

	if(SDL > lightAngleCosine)
	{
		float spotIntensity = pow(SDL, lightDecayExponent);

		//get normal data from the normalMap
		float4 normalData = tex2D(normalSampler, texCoord);
		
		//tranform normal back into [-1,1] range
		float3 normal = 2.0f * normalData.xyz - 1.0f;
		
		//get specular power
		float specularPower = normalData.a * 255;
		
		//get specular intensity from the colorMap
		float specularIntensity = tex2D(colorSampler, texCoord).a;

		//compute diffuse light
		float NDL = max(0, dot(normal,lightVector));
		float3 diffuseLight = NDL * Color.rgb;

		//reflection vector
		float3 reflectionVector = normalize(reflect(-lightVector, normal));
		
		//camera-to-surface vector
		float3 directionToCamera = normalize(cameraPosition - position);
		
		//compute specular light
		float specularLight = specularIntensity * pow( saturate(dot(reflectionVector, directionToCamera)), specularPower);

		attenuation *= spotIntensity;
		
		float4 proj = 0;
		proj = mul(position, lightWorldViewProj);
		
		float2 projectedTexCoords;
		projectedTexCoords[0] =  proj.x/proj.w/2.0f + 0.5f;
		projectedTexCoords[1] = -proj.y/proj.w/2.0f + 0.5f;
		
		float4 moments = tex2D(shadowMapSampler, projectedTexCoords).r;
		float4 mask = tex2D(lightTexSampler, projectedTexCoords);
		
		float offset = 0.001f;
		float len = (proj.z / proj.w) - offset;
		float shadow = step(len, moments.x);
		
		return mask.r * shadow * attenuation  * lightIntensity * float4(diffuseLight.rgb, specularLight);
	}
	else
	{
		return float4(0,0,0,0);
	}
}

technique Technique1
{
	pass Pass1
	{
		ZEnable = false;
		ZWriteEnable = false;

		VertexShader = compile vs_2_0 VertexShaderFunction();
		PixelShader = compile ps_2_0 PixelShaderFunction();
	}
}